#include <linux/fs.h> 	     /* file stuff */
#include <linux/slab.h> //kmalloc()
#include <linux/time.h>
#include <linux/kernel.h>    /* printk() */
#include <linux/errno.h>     /* error codes */
#include <linux/module.h>  /* THIS_MODULE */
#include <linux/cdev.h>      /* char device stuff */
#include <asm/uaccess.h>  /* copy_to_user() */

#include "./gps_device.h"

#define GPS_FPATH "/root/Documents/HomeWork/Hw3/gps-example.txt"
#define MAJOR_NUM 231
#define DEVICE_NAME "GPS"

u64 last_time = 0;
u64 curr_time = 0;
int size_last_time = 0;
struct file* fp;

/*===============================================================================================*/
static ssize_t device_file_read(
	struct file *file_ptr,
	char __user *user_buffer,
	size_t count,
	loff_t *possition
){
	//以ns为单位的时间
	curr_time = (u64)(ktime_get_ns());
	printk("lasttime: %lld, current time: %lld\n", last_time, curr_time);
	if((curr_time - last_time) < 1000000000){
		printk("这次读还没有间隔1s!\n");
		// 为了做到当前时刻数据还未准备好，返回上一时刻的内容，
		// 只需要将当前读取gps-example.txt的偏移量退回到上一次
		// 读取数据之前的位置即可。
		*possition -= size_last_time;
	}
	// 我们需要记录最后一次读取设备文件的时间
	last_time = curr_time;

	printk(KERN_NOTICE "gps_device: Device file is read at offset = %i, trying to read bytes count = %u\n"
			, (int)*possition
			, (unsigned int)count);

	// 调用内核态读文件的函数vfs_read，其各参数的意义为：
	//     @fp :文件指针
	//     @user_buffer：用户态缓冲区
	//     @count：尝试向用户态缓冲区读取的字节数
	//     @possition：文件指针的偏移量指针
	// 我们已经在上方控制过了偏移量position，在这里只需正常
	// 读取文件即可。
	// vfs_read会从fp偏移量*position处读取文件，向用户缓冲区写入文件数据，
	// 并维护了*position的大小。我们无需关注其内部实现原理。
	count = vfs_read(fp, user_buffer, count, possition);
	printk(KERN_NOTICE "gps_device: Device file got read count = %u\n", (unsigned int)(count));

	// 我们需要记录这次成功读取的字节数，以便于在下一次用户尝试读取
	// 数据，而数据并未准备好时，做文件指针position的回退。
	size_last_time = count;

	// 向用户返回成功读取的字节数。
	return count;
}

/*===============================================================================================*/
static struct file_operations simple_driver_fops = {
	.owner   = THIS_MODULE,
	.read    = device_file_read,
};

static int device_file_major_number = MAJOR_NUM;
static const char device_name[] = DEVICE_NAME;

/*===============================================================================================*/
int register_device(void) {

	int result = 0;
	fp = filp_open(GPS_FPATH, O_RDONLY, 0);

	printk("fs file address:0x%p\n", fp);
	if(IS_ERR(fp)){
		printk("can not open fs.\n");
		return -EFAULT;
	}

	printk(KERN_NOTICE "gps_device: register_device() is called.");

	result = register_chrdev(device_file_major_number, device_name, &simple_driver_fops);
	if(result < 0){
		printk(KERN_WARNING "gps_device:  can\'t register char device with errorcode = %i", result);
		return result;
	}

	device_file_major_number = result;
	printk(KERN_NOTICE "gps_device: registered char device with major number = %i\n"
			, MAJOR_NUM );

	return 0;
}

/*-----------------------------------------------------------------------------------------------*/
void unregister_device(void) {
	printk(KERN_NOTICE "gps_device: unregister_device() is called");
	if(device_file_major_number != 0){
		unregister_chrdev(device_file_major_number, device_name);
	}
}
